package cn.m1c.auth.tool;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;

import cn.m1c.auth.common.JwtStatusCode;
import cn.m1c.auth.jwt.JWTAudienceException;
import cn.m1c.auth.jwt.JWTExpiredException;
import cn.m1c.auth.jwt.JWTIssuerException;
import cn.m1c.auth.jwt.JWTSigner;
import cn.m1c.auth.jwt.JWTVerifier;
import cn.m1c.frame.constants.StatusCode;
import cn.m1c.frame.exception.M1CRuntimeException;
import cn.m1c.frame.utils.AssertUtil;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;


/**
 * @author  phil E-mail: s@m1c.cn
 * @date 2016年5月20日
 * @version 
 * @since y:2016
 * @description 使用jwt方式来替代传统的token  有项目引入时 拷贝本代码 然后替换掉 bsecret 变为私有工具
 * 	如果采用rsa进一步加密内容，请再通过
 */
@SuppressWarnings("restriction")
public class JwtUtil {
	private static Logger logger = LoggerFactory.getLogger(JwtUtil.class);
	/**
	 * 使用过程中一定要重新初始化如下加密串
	 */
	private static final byte[] BSECRET = new byte[]{73, 83, 121, 104, 81, 85, 65, 
			70, 94, 99, 101, 82, 105, 118, 100, 76, 99, 69, 120, 36, 72, 99, 
			35, 67, 64, 73, 42, 82, 69, 72, 38, 118, 105, 67, 101, 82, 72, 111, 
			71, 112, 98, 120, 89, 86, 90, 69, 111, 120, 64, 112, 122, 81, 66, 89, 
			110, 97, 122, 103, 33, 105, 75, 114, 33, 71};
	 private static JWTSigner signer = new JWTSigner(BSECRET);
	 private static JWTVerifier verifier = new JWTVerifier(BSECRET);
	 /***
	  * 如下公钥和秘钥 在使用过程中请直接替换，直接使用该类中的方法  
	  *  generatorKeyPair()
	  */
	 private static String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu3qupGlHjoeiLJNjDjHjCIdMh5Htu8krAt57OcAXa2b0FYIoPLQl9YRER+Hu2HzBU1fs04Dtudh7+DlvfWeVkAnS0zY7Fb3d7n5716eh7ZlrdBwYz2636LsR1O7+C7SJMhpZVP+3JwD5t7UyLkYhL32TP32/tEKETmULu66GTXNsRZdH+hVVxbYJOichzdfRMxGIWdx6e1mIAvjSkc2i9UKc08q6BEPwR8hj+cNmjB0BtMZ5pgl9AwfFTOXGId5wbBLzxdauktbNO4RHUlTYZoBh6NpkB+2FgxrMHPFqMl3tEYc8xsOhtkD+tZyb461k1iPQlAijOEK09v8oxgvnEwIDAQAB";
	 private static String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7eq6kaUeOh6Isk2MOMeMIh0yHke27ySsC3ns5wBdrZvQVgig8tCX1hERH4e7YfMFTV+zTgO252Hv4OW99Z5WQCdLTNjsVvd3ufnvXp6HtmWt0HBjPbrfouxHU7v4LtIkyGllU/7cnAPm3tTIuRiEvfZM/fb+0QoROZQu7roZNc2xFl0f6FVXFtgk6JyHN19EzEYhZ3Hp7WYgC+NKRzaL1QpzTyroEQ/BHyGP5w2aMHQG0xnmmCX0DB8VM5cYh3nBsEvPF1q6S1s07hEdSVNhmgGHo2mQH7YWDGswc8WoyXe0RhzzGw6G2QP61nJvjrWTWI9CUCKM4QrT2/yjGC+cTAgMBAAECggEAAhLwXadAgGYluCKFRQHuRgjhItrNa3dlP2ngqRnj6G9CIf7HnSxNAzNMYhMwxjpTFS3y08AoYRbxACT6g8Oilu5U47tE2g+4pcvGaPNUykEZPDaXW6wY4r03DEj8jHqZzfEWrecKSqcrHAooHPTvpwvXlis49LCLsCZUA+aBW9f3MMg2n1uLltmiQ5QXibSC6MFaD5rwTrog5cDruUa9KgUep/p1qPXoUX1acyvuS9yVRzC0FLB0c1SOHQZLXlEcRDfT28Z0empbzyIpe8QjouSDE8CvG4BHPpZOohdBG9wPwDPjnFdiKOw+gFvby0FIUBF/ynvz9WcEhlEWNbzO4QKBgQDcNKFqdViv9zV8fk7AXurZxd62lJb+27Ms30hdFkeBDjr/87WnwBUSfleiH0UlDFE38QBkvdqSYe1FqeE+qBr0/pcvMLq3+fmEBuvfdW9MaA53FMxiycRhfyWI7XRC3cbRxzYC/w+l5RdZL1BkK/Si72MjladeEm4ONth46D9xsQKBgQDZ9DdmUi8SbEK3YmJnJ5TTq0CnxDZUjBM+lGS56jwto9b/tatEnJyVQqu/MkgQ2B1OQjD57rP8tqQNYTJLO3LEwdeMOeXZVdUV1Oobni1wFmdVuagB4je1MEEaFcCGoMzqd/6qVrZHReWzb5JuaJl73O6g/nhrKE+Onxi63+kyAwKBgHd+C9WEJgMex6RtufhDyXcxG4DCMwnLWHhSFaeFznlmqnd33SxRXF3zt0QfadsHyw7rNYZ1S6VBhIrX+4WPnUky2gFelQP+nvqY8ugIhsPUd30BCUPWLz2kzE54ZC1gwWHWkyWfGPOx1DCuLxacj5zXAFov/Glc4OHPuzoo9TyRAoGAMoAMig/Gylz9RGJubHpWWIkYjQbAk+x01Q4rHIyybtzEhzE/wsEK4ua0xrNO0WBRwf1CPdbaK1biQhtgPOBEYmSLnfkYUbg6jDluGQIYgx0dylYfIHia8ceBfhrGJiuB713oSu2WhIMkiuPyKNThozp4rCLqIeIQNPqWD06sSKMCgYEAg2SnfEoXvM8akPc1dlNnmgbMgJORM/HCg4xmlBd9G99MHYWimgIIUNN4/Nq+kH+fzHX8ryVLbnQOulGsknBy3ZL4D8s1ArTTiuUZRnyC13M/eOS0QOPgEwBT5kftoubge4g+yHg0wKjzBtAeGvyu5KWIUOeP/pA30bqpCXZvfUw=";
	 /**
	  * @param object  入参为user对象就可以
	  * @return 签名之后的数据
	  */
	 public static String sign(Object object){
		 HashMap<String, Object> claims = new HashMap<String, Object>();
	        claims.put("user", object);
	        claims.put("now", Calendar.getInstance().getTimeInMillis());
	        String token = signer.sign(claims,new JWTSigner.Options().setJwtId(true).
	        		setExpirySeconds(12*60*60).setNotValidBeforeLeeway(2).setIssuedAt(true));
	       return token;
	 }
	 
	public static Object unSign(String token) {
		Map<String, Object> decoded = new HashMap<String, Object>();
		try {
			decoded = verifier.verify(token);
		} catch (InvalidKeyException e) {
			logger.error("无效key",e);
			throw new M1CRuntimeException(StatusCode.forbidden);
		} catch (NoSuchAlgorithmException e) {
			logger.error("参数错误",e);
			throw new M1CRuntimeException(StatusCode.forbidden);
		} catch (IllegalStateException e) {
			logger.error("不合适的状态",e);
			throw new M1CRuntimeException(StatusCode.forbidden);
		} catch (SignatureException e) {
			logger.error("签名错误",e);
			throw new M1CRuntimeException(StatusCode.forbidden);
		} catch (IOException e) {
			logger.error("io异常",e);
			throw new M1CRuntimeException(StatusCode.forbidden);
		} catch (JWTExpiredException e) {
			logger.error("token过期",e);
			throw new M1CRuntimeException(StatusCode.timeout);
		} catch (JWTIssuerException e) {
			logger.error("issuser无效",e);//common_error_illegal_error 非法操作
			throw new M1CRuntimeException(StatusCode.forbidden);
		} catch (JWTAudienceException e) {
			logger.error("用户无效",e);
			throw new M1CRuntimeException(StatusCode.forbidden);
		}  
		return decoded.get("user");
	}
	
	/**
	  * @param String  入参为user对象json序列化之后的字符串
	  * @return 签名之后的数据 ,如果用在项目中建议将该数据存储在redis中
	  */
	public static String signRsa(String jsonStr) {
		// rsa加密
		AssertUtil.isTrue(jsonStr.getBytes().length <= 245, JwtStatusCode.rsa_legth_error);
		PublicKey rsaPublicKey = null;
		try {
			rsaPublicKey = restorePublicKey(decryptBASE64(publicKey));
		} catch (Exception e) {
			e.printStackTrace();
		}
		byte[] encodedText = RSAEncode(rsaPublicKey, jsonStr.getBytes());
		String publicSecStr = Base64.encodeBase64String(encodedText);
		
		HashMap<String, Object> claims = new HashMap<String, Object>();
		claims.put("user", publicSecStr);
		claims.put("now", Calendar.getInstance().getTimeInMillis());
		String token = signer.sign(claims, new JWTSigner.Options().setJwtId(true).setExpirySeconds(12 * 60 * 60)
				.setNotValidBeforeLeeway(2).setIssuedAt(true));
		return token;
	}
	 /**
	  * 与signRsa配套使用
	  * @param token
	  * @return
	  */
	 public static String unSignRsa(String token) {
			Map<String, Object> decoded = new HashMap<String, Object>();
			try {
				decoded = verifier.verify(token);
			} catch (InvalidKeyException e) {
				logger.error("无效key",e);
				throw new M1CRuntimeException(StatusCode.forbidden);
			} catch (NoSuchAlgorithmException e) {
				logger.error("参数错误",e);
				throw new M1CRuntimeException(StatusCode.forbidden);
			} catch (IllegalStateException e) {
				logger.error("不合适的状态",e);
				throw new M1CRuntimeException(StatusCode.forbidden);
			} catch (SignatureException e) {
				logger.error("签名错误",e);
				throw new M1CRuntimeException(StatusCode.forbidden);
			} catch (IOException e) {
				logger.error("io异常",e);
				throw new M1CRuntimeException(StatusCode.forbidden);
			} catch (JWTExpiredException e) {
				logger.error("token过期",e);
				throw new M1CRuntimeException(StatusCode.timeout);
			} catch (JWTIssuerException e) {
				logger.error("issuser无效",e);//common_error_illegal_error 非法操作
				throw new M1CRuntimeException(StatusCode.forbidden);
			} catch (JWTAudienceException e) {
				logger.error("用户无效",e);
				throw new M1CRuntimeException(StatusCode.forbidden);
			}  
			String originalJwtStr = decoded.get("user").toString();
			//rsa解密
			PrivateKey rsaPrivateKey = null;
			try {
				rsaPrivateKey = restorePrivateKey(decryptBASE64(privateKey));
			} catch (Exception e) {
				e.printStackTrace();
			}
	        String original = RSADecode(rsaPrivateKey, Base64.decodeBase64(originalJwtStr));
	        return  original;
		}
	
	/**
     * BASE64加密
     * @param key
     * @return
     * @throws Exception
     */
	public static String encryptBASE64( byte[] key) throws Exception{
        return (new BASE64Encoder()).encodeBuffer(key);
    }
	/**
     * BASE64解密
     * @param key
     * @return
     * @throws Exception
     */
	public static byte[] decryptBASE64( String key ) throws Exception{
        return (new BASE64Decoder()).decodeBuffer(key);
    }
	/**
     * 还原公钥，X509EncodedKeySpec 用于构建公钥的规范
     * 
     * @param keyBytes
     * @return
     */
    public static PublicKey restorePublicKey(byte[] keyBytes) {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        try {
            KeyFactory factory = KeyFactory.getInstance("RSA");
            PublicKey publicKey = factory.generatePublic(x509EncodedKeySpec);
            return publicKey;
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 还原私钥，PKCS8EncodedKeySpec 用于构建私钥的规范
     * 
     * @param keyBytes
     * @return
     */
    public static PrivateKey restorePrivateKey(byte[] keyBytes) {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
                keyBytes);
        try {
            KeyFactory factory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = factory
                    .generatePrivate(pkcs8EncodedKeySpec);
            return privateKey;
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return null;
    }
    /** 貌似默认是RSA/NONE/PKCS1Padding，未验证 */
    public static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding";
    /**
     * 加密，三步走。
     * 
     * @param key
     * @param plainText
     * @return
     */
    public static byte[] RSAEncode(PublicKey key, byte[] plainText) {
        try {
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            return cipher.doFinal(plainText);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException
                | InvalidKeyException | IllegalBlockSizeException
                | BadPaddingException e) {
            e.printStackTrace();
        }
        return null;

    }
    /**
     * 解密，三步走。
     * 
     * @param key
     * @param encodedText
     * @return
     */
    public static String RSADecode(PrivateKey key, byte[] encodedText) {

        try {
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, key);
            return new String(cipher.doFinal(encodedText));
        } catch (NoSuchAlgorithmException | NoSuchPaddingException
                | InvalidKeyException | IllegalBlockSizeException
                | BadPaddingException e) {
            e.printStackTrace();
        }
        return null;

    }
	public static void main(String[] args) throws Exception {
		HashMap<String, String> map = new HashMap<>();
		map.put("user", "zhangsan");
		String s = JwtUtil.signRsa(JSON.toJSONString(map));
		System.out.println(s);
		s = JwtUtil.signRsa(JSON.toJSONString(map));
		System.out.println(s);
		//将该字符串进行md5
		
		String o = JwtUtil.unSignRsa(s);
		System.out.println(o);
//		//rsa加密
//		if(JSON.toJSONString(map).getBytes().length>245){
//			System.out.println("您输入的数据过大，请缩减之后重新录入");
//			return;
//		}
//		PublicKey rsaPublicKey = restorePublicKey(decryptBASE64(publicKey));
//		byte[] encodedText = RSAEncode(rsaPublicKey, JSON.toJSONString(map).getBytes());
//		String publicSecStr = Base64.encodeBase64String(encodedText);
//		System.out.println("加密字符串："+publicSecStr);
//		//jwt加密
//		String jwtStr = JwtUtil.sign(publicSecStr);
//		System.out.println("jwt加密："+jwtStr);
//		//加密之后的串才是要用的
//		//通过网络传输
//		
//		//网络拿到数据
//		//jwt解密
//		String originalJwtStr = JwtUtil.unSign(jwtStr).toString();
//		System.out.println("jwt解密："+originalJwtStr);
//		//rsa解密
//		PrivateKey rsaPrivateKey = restorePrivateKey(decryptBASE64(privateKey));
//        String original = RSADecode(rsaPrivateKey, Base64.decodeBase64(originalJwtStr));
//        System.out.println("解密字符串：" + original);
//        
//        Map orgginalMap = JSON.parseObject(original, Map.class);
//        System.out.println(orgginalMap.get("user"));
	}
	
	 /** 
     * 生成密钥对 
     * 
     */  
    @SuppressWarnings("unused")
	private static void generatorKeyPair() throws Exception {  
    	String ALGORITHM_RSA = "RSA";  
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALGORITHM_RSA);  
        keyPairGen.initialize(1024);  
        KeyPair keyPair = keyPairGen.generateKeyPair();  
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();  
        byte[] keyBs = rsaPublicKey.getEncoded();  
        publicKey = encryptBASE64(keyBs);  
        System.out.println("生成的公钥：\r\n" + publicKey);  
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();  
        keyBs = rsaPrivateKey.getEncoded();  
        privateKey = encryptBASE64(keyBs);  
        System.out.println("生成的私钥：\r\n" + privateKey);  
    }  
}
